#include <string.h>
#include <stdlib.h>

#include "lib.h"

#include "mysteryguts.h"
#include "shue.h"
#include "vphand.h"
#include "cardmgr.h"
#include "serv.h"
#include "pte_mystery.h"	// PTE_xxx -> PayTableEvents...i.e. VPEVT_xxx
#include "pt_mystery.h"

#include "getrand.h"

// MAXRANGE:  
//
#define MAXRANGE	RAND_MAX	// sum of all weights on each wheel equals this

#define BLANK			-1
#define NUMFREQS		3
#define NUMITEMS		18

typedef struct _wheel
{
	int32  value;
	uint32 weight;
	uint32 topRange;
} _wheel;

// Macro Anchor
static _wheel wheels[NUMFREQS][NUMITEMS] =
{
	//
	// (98.97%)
	//
		{             
            VPEVT_ROYALFLUSH,		536870912 , 0, 
            BLANK,							0 , 0, 
            VPEVT_STRAIGHTFLUSH,	738197504 , 0, 
            BLANK,							0 , 0, 
            VPEVT_4OFAKIND,			671088640 , 0, 
            BLANK,							0 , 0, 
            VPEVT_FULLHOUSE,		402653184 , 0, 
            BLANK,							0 , 0, 
            VPEVT_FLUSH,			738197504 , 0, 
            BLANK,							0 , 0, 
            VPEVT_STRAIGHT,			671088640 , 0, 
            BLANK,							0 , 0, 
            VPEVT_3OFAKIND,			335544320 , 0, 
            BLANK,							0 , 0, 
            VPEVT_2PAIR,			134217728 , 0, 
            BLANK,							0 , 0, 
            VPEVT_XXX_OR_BETTER,	 67108864 , 0, 
            BLANK,							0 , 0, 
		},         
	//             
	// (98.97%)       
	//                    
		{             
            VPEVT_ROYALFLUSH,		536870912 , 0, 
            BLANK,							0 , 0, 
            VPEVT_STRAIGHTFLUSH,	738197504 , 0, 
            BLANK,							0 , 0, 
            VPEVT_4OFAKIND,			671088640 , 0, 
            BLANK,							0 , 0, 
            VPEVT_FULLHOUSE,		402653184 , 0, 
            BLANK,							0 , 0, 
            VPEVT_FLUSH,			738197504 , 0, 
            BLANK,							0 , 0, 
            VPEVT_STRAIGHT,			671088640 , 0, 
            BLANK,							0 , 0, 
            VPEVT_3OFAKIND,			335544320 , 0, 
            BLANK,							0 , 0, 
            VPEVT_2PAIR,			134217728 , 0, 
            BLANK,							0 , 0, 
            VPEVT_XXX_OR_BETTER,	 67108864 , 0, 
            BLANK,							0 , 0, 
		},   
	//             
	// (98.97%)       
	//             
		{             
            VPEVT_ROYALFLUSH,		536870912 , 0, 
            BLANK,							0 , 0, 
            VPEVT_STRAIGHTFLUSH,	738197504 , 0, 
            BLANK,							0 , 0, 
            VPEVT_4OFAKIND,			671088640 , 0, 
            BLANK,							0 , 0, 
            VPEVT_FULLHOUSE,		402653184 , 0, 
            BLANK,							0 , 0, 
            VPEVT_FLUSH,			738197504 , 0, 
            BLANK,							0 , 0, 
            VPEVT_STRAIGHT,			671088640 , 0, 
            BLANK,							0 , 0, 
            VPEVT_3OFAKIND,			335544320 , 0, 
            BLANK,							0 , 0, 
            VPEVT_2PAIR,			134217728 , 0, 
            BLANK,							0 , 0, 
            VPEVT_XXX_OR_BETTER,	 67108864 , 0, 
            BLANK,							0 , 0, 
		}
};



static CardMgr stdMgr;

Shue*
MysteryGuts::newShue(const ShueGameInfo &sgi)
{
	return new Shue(&stdMgr, 1, sgi);
}

CardMgr*
MysteryGuts::getCardMgr()
{
	return &stdMgr;
}

void
MysteryGuts::CheckWeights()
{
	wheels[payFreq][0].topRange = wheels[payFreq][0].weight - 1;	// includes 0

	for (int j = 1; j < NUMITEMS; j++)
		wheels[payFreq][j].topRange =
			wheels[payFreq][j - 1].topRange +
			wheels[payFreq][j].weight;

	if (wheels[payFreq][NUMITEMS - 1].topRange != MAXRANGE)
	{
		DEBUGMSG(("MysteryPoker: wheel weights don't add up "));
		exit(-1);
	}

}

int32
MysteryGuts::CalcPayout(int32* pEvent, VPHand* pHand, int32 Bet,
	CalcPayoutMode, uint8* subGameData, int32 length)
{
	*pEvent = classifyHand(pHand);
	
	if (*pEvent == VPEVT_LOSER)
	{
		DEBUGMSG(("LOSER"));
		return 0;
	}

	if (!subGameData || length < 2)
	{
		DEBUGMSG(("ERROR!!! This should never happen! Mystery Poker allways has subgame data"));
	}

	if (subGameData[1] >=0 && subGameData[1] < NUMITEMS && wheels[payFreq][subGameData[1]].value == *pEvent)
	{
		DEBUGMSG(("SPECIAL"));	
		return sBPayTable[payFreq][Bet-1][*pEvent];
	}
	else
	{
		DEBUGMSG(("NORMAL"));
		return PTBLENTRY(Bet-1, *pEvent);
	}
}



int32
MysteryGuts::classifyHand(VPHand* pHand)
{
	uint8 counts[13];
	int32 i;
	uint8* probe, *first;
	bool8 flush = true;

	if (!pHand || (pHand && pHand->getCount() != 5))
		return VPEVT_LOSER;

	memset(counts, 0, 13);	// 2:index 0, 3:index 1 ... A:index 12

	for (i = 0 ; i < pHand->getCount(); i++)
	{
		++counts[stdMgr.getRank(pHand->getCardAt(i)) - 2];

		if (i > 0 && flush)
			flush = (bool8)(stdMgr.getSuit(pHand->getCardAt(i)) ==
				stdMgr.getSuit(pHand->getCardAt(i - 1)));
	}

	// pair, 2 pairs, 3-of-a-kind, 4-of-a-kind, full house
	//
	if (memchr(counts, 4, 13))
		return VPEVT_4OFAKIND;

	if (memchr(counts, 3, 13))
		if (memchr(counts, 2, 13))
			return VPEVT_FULLHOUSE;
		else
			return VPEVT_3OFAKIND;

	probe = (uint8*)memchr(counts, 2, 13);
	if (probe)
	{
		if (probe < counts+(13-1) && memchr(probe+1, 2, 13 - (probe-counts+1)))
			return VPEVT_2PAIR;
		else if (probe - counts >= minPair-2)
			return VPEVT_XXX_OR_BETTER;
	}

	// straights
	//
	first = probe = (uint8*)memchr(counts, 1, 13);
	i = 0;
	while (probe < counts + 13)
		if (*probe++ == 1)
			i++;
		else
			break;

	if (i == 5)
	{
		if (flush)
			if (first == counts + 10-2)
				return VPEVT_ROYALFLUSH;
			else
				return VPEVT_STRAIGHTFLUSH;

		return VPEVT_STRAIGHT;
	}

	if (i == 4 &&								// ace-low straights
		counts[CardMgr::ACE-2] && first == counts)
	{
		if (flush)
			return VPEVT_STRAIGHTFLUSH;

		return VPEVT_STRAIGHT;
	}

	return flush ? VPEVT_FLUSH : VPEVT_LOSER;
}

void 
MysteryGuts::restoreSubGameData(uint8* subGameData, int32* length)
{
	if (*length != 2)
		return;
	
	//the set is only called to restore a saved game
	//when restoring a saved game we thow out the spin data
	//in the database and don't spin the wheel
	
	subGameData[0] = false;			//don't spin the wheel
}

void
MysteryGuts::createSubGameData(uint8* subGameData, int32* length, int32 maxlength)
{
	subGameData[0] = true;			//spin the wheel
	subGameData[1] = (uint8)findIndex(getrand());
	*length = 2;

#ifdef DEBUG
	int32 wheelvalue = wheels[payFreq][subGameData[1]].value;
	
	char *textvalue;
	switch(wheelvalue)
	{
		case BLANK:
			textvalue = "Blank";
			break;
			
		case VPEVT_XXX_OR_BETTER:
			textvalue = "Jacks or Better";
			break;
			
		case VPEVT_2PAIR:
			textvalue = "2 pair";
			break;
			
		case VPEVT_3OFAKIND:
			textvalue = "3 of a kind";
			break;
			
		case VPEVT_STRAIGHT:
			textvalue = "Straight";
			break;
			
		case VPEVT_FLUSH:
			textvalue = "Flush";
			break;
			
		case VPEVT_FULLHOUSE:
			textvalue = "Fullhouse";
			break;
			
		case VPEVT_4OFAKIND:
			textvalue = "4 of a kind";
			break;
			
		case VPEVT_STRAIGHTFLUSH:
			textvalue = "Straight Flush";
			break;
			
		case VPEVT_ROYALFLUSH:
			textvalue = "Royal Flush";
			break;
			
		default:
			textvalue = "Error: unknown spin value";
	}

	DEBUGMSG(("Spin stopping on: %s.",textvalue));
#endif

}

int32
MysteryGuts::findIndex(uint32 v)
{
	int l, h, m;

	l = 0;
	h = NUMITEMS - 1;

	while (l <= h)
	{
		m = (l + h) >> 1;

		if (v < wheels[payFreq][m].topRange)
			h = m - 1;
		else if (v > wheels[payFreq][m].topRange)
			l = m + 1;
		else
		{
			l = m;
			break;
		}
	}

	// at this point "l" contains index of greatest wheel weight value <= v
	// 

	// following shouldn't happen...only happens if last item
	// on wheel isn't RAND_MAX.  Just in case.
	//
	if (l >= NUMITEMS)
		l = NUMITEMS - 1;

	return l;
}